#ifndef __COMPILER_HPP__
#define __COMPILER_HPP__
// 编译模块
#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include "../common/util.hpp"
#include "../common/log.hpp"

namespace ns_compiler
{
    using namespace ns_util;
    using namespace ns_log;

    class Compiler
    {
    public:
        Compiler()
        {}
        ~Compiler()
        {}

        // 编译函数  编译临时文件
        // 输入：需要编译的临时文件名（存在./temp/文件名.cpp文件，不需要带cpp/cc后缀）
        // 输出：编译成功true，false失败
        static bool compile(std::string &file_name)
        {
            pid_t childPid = fork();
            if (childPid < 0)
            {
                // 子进程创建失败
                LOG(ERROR) << "内部错误，当前子进程无法创建" << "\n";
                return false;
            }
            if (childPid == 0)
            {
                // 子进程
                // 如果编译失败，需要将错误信息输入到错误文件内，利用重定向
                umask(0);  // 防止平台的影响，影响文件权限
                int errFd = open(PathUtil::CompileError(file_name).c_str(), O_CREAT | O_WRONLY, 0644);  // rw_r__r__
                if (errFd < 0)
                {
                    // 异常，终止程序
                    LOG(ERROR) << "内部错误，错误输出文件打开/创建失败" << "\n";
                    exit(1);
                }
                // 打开成功重定向
                dup2(errFd, 2);  // 将标准错误重新向到错误文件内，这样出错就可以将错误信息写入其中

                execlp("g++", "g++", "-o", PathUtil::Exe(file_name).c_str(), PathUtil::Src(file_name).c_str(), "-D", "COMPILE_ONLINE", NULL);  // p默认从环境变量下搜索
                LOG(ERROR) << "g++执行失败，检查参数是否传递正确" << "\n";
                exit(2);  // 如果执行这一步说明上面的exec程序替换函数执行失败
            }
            // 父进程
            waitpid(childPid, nullptr, 0);  // 阻塞等待子进程
            // 判断是否编译成功可以根据是否生成exe可执行文件进行判断
            if (FileUtil::IsFileExist(PathUtil::Exe(file_name)))
            {
                // .exe文件存在
                LOG(INFO) << "编译成功！" << "\n";
                return true;  // 编译成功
            }
            LOG(ERROR) << "编译失败！" << "\n";
            return false;  // exe文件不存在，编译失败
        }
    };
}

#endif